-- Matías Blanco & Sergio Pérez
-- Hippies - Inventadas

with Ada.Text_Io ; use Ada.Text_Io;
with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;
with Ada.Numerics.Discrete_Random;

procedure HippiesCitasAda is

   -- Variables y demás
   MAX_HIPPIES: constant Integer := 12;

   -- Aleatorio discreto

   subtype valores is Integer range 1..MAX_HIPPIES;
   package NumeroAleatorio is new Ada.Numerics.Discrete_Random(valores);

   use NumeroAleatorio;

   F: Generator;

   Aux1: Integer;
   Aux2: Integer;
   T: Boolean := False;

   FIN_JUEGO: Boolean:= False;

   type THuevos is array (1..MAX_HIPPIES) of Boolean;

   task HippieSabio is -- Este sabe todo lo que hay :)
      entry Tengo(Id: in Integer);
      entry No_Tengo(Id: in Integer);
      entry Tiene(Id: in Integer; OK_Tiene: in out Boolean);
      entry EstoyEliminado(Id: in Integer);
      entry EstaEliminado(Id: in Integer; OK_ELIMINADO: in out Boolean);
      entry N_Elim(N: in out Integer);
      entry SubitaLanzada;
      entry IS_Subita(L: in out Boolean);
   end HippieSabio;

   task body HippieSabio is
      Hippie_Tiene_Huevo: THuevos := (others => False);
      Hippie_Eliminado: THuevos:= (others => False);
      Jugando: Integer:= MAX_HIPPIES;
      Subita: Boolean := False;
   begin
      loop
         select
            accept Tengo(Id: in Integer) do
               Hippie_Tiene_Huevo(Id):= True;
            end Tengo;
         or
            accept No_Tengo(Id: in Integer) do
               Hippie_Tiene_Huevo(Id):= False;
            end No_Tengo;
         or
            accept Tiene(Id: in Integer; OK_Tiene: in out Boolean) do
               OK_Tiene := Hippie_Tiene_Huevo(Id);
            end Tiene;
         or
            accept EstoyEliminado(Id: in Integer) do
               Hippie_Eliminado(Id):= True;
            end EstoyEliminado;
         or
            accept EstaEliminado(Id: in Integer; OK_ELIMINADO: in out Boolean) do
               OK_ELIMINADO := Hippie_Eliminado(Id);
            end EstaEliminado;
         or
            accept N_Elim(N: in out Integer) do
               Jugando := 0;
               for I in 1..MAX_HIPPIES loop
                  if (not Hippie_Eliminado(I)) then
                     Jugando:= Jugando + 1;
                  end if;
               end loop;
               N:= Jugando;
            end N_Elim;
         or
            accept SubitaLanzada do
               Subita := True;
            end SubitaLanzada;
         or
            accept Is_Subita(L: in out Boolean) do
               L := Subita;
            end Is_Subita;
         end select;
      end loop;

   end HippieSabio;


   task MembranaCentro is
      entry Tocar(Id: in Integer);
      entry Soltar(Id: in Integer);
   end MembranaCentro;

   task body MembranaCentro is
      Tocada: Boolean := False;
   begin
      loop
         select
            when (not Tocada) =>
               accept Tocar(Id: in Integer) do
                  Put_Line("El hippie " & Integer'Image(Id) & " toca la mebrana");
                  Tocada:= True;
               end Tocar;
         or
            accept Soltar(Id: in Integer) do
               Put_Line("El hippie " & Integer'Image(Id) & " suelta la mebrana");
               Tocada := False;
            end Soltar;
         or
            terminate;
         end select;
      end loop;
   end MembranaCentro;

   task CestaHuevos is
      entry CogerHuevo(Id: in Integer);
   end CestaHuevos;

   task body CestaHuevos is
   begin
      loop
         select
            accept CogerHuevo(Id: in Integer) do
               Put_Line("El hippie " & Integer'Image(Id) & " esta cogiendo un huevo");
               delay (1.0);
               Put_Line("El hippie " & Integer'Image(Id) & " consigue un huevo");
            end CogerHuevo;
      or
         terminate;
         end select;
      end loop;
   end CestaHuevos;

   task type Hippie is
      entry Init(Idd: in Integer; N: in Integer);
      entry Lanzar_Hacia (Idd: in Integer; OK: in out Boolean);
      entry MuerteSubita (Idd: in Integer; OK: in out Boolean);
   end Hippie;

   Hippies: array (1..MAX_HIPPIES) of Hippie;

   task body Hippie is
      Id: Integer;
      Huevos: Integer;
      Jugando: Boolean := True;
      Aux_Random: Integer := 0;
      H_Tiene_Otro: Boolean;

      IS_OK: Boolean := False;

      EliminadoManda: Boolean := False;

      N_Jugando: Integer:= MAX_HIPPIES;

      DebeHuevo: Boolean:=False;


   begin
      accept Init(Idd: in Integer; N: in Integer) do
         if (N = 1) then
            HippieSabio.Tengo(Idd);
         end if;
         Put_Line("El hippie " & Integer'Image(Idd) & " empieza con" & Integer'Image(N) & " huevo/s");
         Huevos :=N;
         Id:= Idd;
      end Init;



      N_Jugando := MAX_HIPPIES;

      while (N_Jugando > 1 ) loop


         HippieSabio.N_Elim(N_Jugando);
         Put_Line("(***) Hippies en juego = " & Integer'Image(N_Jugando));

         if (N_Jugando = 2 and Jugando and MuerteSubita'Count = 0) then
            Put_Line("SUBITA para  el HIPPIE " & Integer'Image(Id));
            for I in 1..MAX_HIPPIES loop
                     HippieSabio.EstaEliminado(I, EliminadoManda);
                     if (not EliminadoManda and I/= Id) then
                        Put_Line("Mandando subita a " & Integer'Image(I));
                        Hippies(I).MuerteSubita(Id,IS_OK);
                        if (IS_OK) then
                           Put_Line("****************************************************************");
                           Put_Line("El hippie " & Integer'Image(Id) & " tira a matar  y GANA!!");
                           Put_Line("****************************************************************");
                        else
                           HippieSabio.N_Elim(N_Jugando);
                           if (N_Jugando > 1) then
                              HippieSabio.EstoyEliminado(Id);
                              Jugando := False;
                           end if;
                           abort Hippie;
                        end if; -- ok? ganador
                     end if; -- eliminado?
                  end loop;
               end if; -- Jugando = 2

         select
            when (MuerteSubita'Count = 0) =>
            accept Lanzar_Hacia(Idd: in Integer; OK: in out Boolean) do
               if (Huevos = 1 or not Jugando) then
                  OK:= False;
                  Put_Line("El hippie " & Integer'Image(Id) & " recibe un huevo de " & Integer'Image(Idd) & " pero SIN CUMPLIR LAS REGLAS...");
               elsif (Random(F) < (MAX_HIPPIES /6) +1) then
                  OK:= True;
                  Put_Line("El hippie " & Integer'Image(Id) & " recibe un huevo de " & Integer'Image(Idd) & " pero lo rompe");
                  Put_Line("DEBE un Huevo el Hippie " & Integer'Image(Id));
                  Jugando := False;
                  HippieSabio.EstoyEliminado(Id);
                  DebeHuevo := True;
               else
                  Put_Line("El hippie " & Integer'Image(Id) & " recibe un huevo de " & Integer'Image(Idd));
                  Huevos :=1;
                  OK:= True;
                  HippieSabio.Tengo(Id);

               end if;
            end Lanzar_Hacia;
            -- Aqui manda...----------------
            HippieSabio.N_Elim(N_Jugando);
            if (DebeHuevo and N_Jugando > 1 ) then
               Put_Line("El hippie " & Integer'Image(Id) & " debe un huevo... aun.. buscando a quien mandarlo ");
               EliminadoManda:= True;
               H_Tiene_Otro:= True;
               while ((EliminadoManda or H_Tiene_Otro or Aux_Random = Id) and N_Jugando > 2 ) loop
                  Put("P ");
                  HippieSabio.N_Elim(N_Jugando);
                  Aux_Random := Random(F);
                  HippieSabio.Tiene(Aux_Random, H_Tiene_Otro);
                  HippieSabio.EstaEliminado(Aux_Random, EliminadoManda);
               end loop;
               Put_Line("El hippie " & Integer'Image(Id) & " que debia un huevo... le pasa uno a " & Integer'Image(Aux_Random));
               Hippies(Aux_Random).Lanzar_Hacia(Id, IS_OK);
               DebeHuevo := False;
               -- Se lo pasa si o si...
            end if;

            if (Lanzar_Hacia'Count = 0 and MuerteSubita'Count=0 and Jugando) then -- Si nadie más nos ha mandado!!!

--               HippieSabio.N_Elim(N_Jugando);
--               Put_Line("(***) Hippies en juego = " & Integer'Image(N_Jugando));

               if (N_Jugando > 2) then
                  Put_Line("El hippie " & Integer'Image(Id) & " se dispone a pasar...");
                  MembranaCentro.Tocar(Id);
                  --
                  EliminadoManda := True;
                  H_Tiene_Otro := True;
                  ------------------------------- ojo
                  while ((EliminadoManda or H_Tiene_Otro or Aux_Random = Id) and N_Jugando > 2 ) loop
--                     Put("    ^"&Integer'Image(N_Jugando)&"^   ");
                     HippieSabio.N_Elim(N_Jugando);
                     Aux_Random := Random(F);
                     HippieSabio.Tiene(Aux_Random, H_Tiene_Otro);
                     HippieSabio.EstaEliminado(Aux_Random, EliminadoManda);
                  end loop;

                  Put_Line("El hippie " & Integer'Image(Id) & " va a pasar a " & Integer'Image(Aux_Random));
                  --
                  delay 0.2;
                  MembranaCentro.Soltar(Id);
                  HippieSabio.N_Elim(N_Jugando);
                  if (N_Jugando > 2) then
                     Put_Line("El hippie " & Integer'Image(Id) & " va a pasar a " & Integer'Image(Aux_Random));
                     if (Lanzar_Hacia'Count = 0) then
                        Hippies(Aux_Random).Lanzar_Hacia(Id, IS_OK);
                        HippieSabio.No_Tengo(Id);
                     else
                       Put_Line("El hippie " & Integer'Image(Id) & " iba a pasar a " & Integer'Image(Aux_Random) & " pero no pasa porque le quieren mandar otro");
                     end if;
               end if;
                  --
                  --
                  Put_Line("El hippie " & Integer'Image(Id) & " suelta la membrana");
                  HippieSabio.N_Elim(N_Jugando);
                  if (not IS_OK and N_Jugando > 1 ) then
                     Put_Line("El hippie " & Integer'Image(Id) & " está ELIMINADO");
                     Jugando:= False;
                     HippieSabio.EstoyEliminado(Id);
                     --
                     HippieSabio.N_Elim(N_Jugando);

                     Put_Line("(***) Hippies en juego = " & Integer'Image(N_Jugando));

                     --DebeHuevo := True;
                     --
--                     HippieSabio.N_Elim(N_Jugando);
--                     if (N_Jugando > 2) then
--                        EliminadoManda := True;
--                        H_Tiene_Otro := True;
--                        while (EliminadoManda or H_Tiene_Otro) loop
--                           Aux_Random := Random(F);
--                           HippieSabio.Tiene(Aux_Random, H_Tiene_Otro);
--                           HippieSabio.EstaEliminado(Aux_Random, EliminadoManda);
--                        end loop;
                        --
--                        Hippies(Aux_Random).Lanzar_Hacia(Id, IS_OK);
--                     end if; -- N Jugando > 2
--                     Put("*1*********");
                  end if; -- not OK
--                  Put("*2*");
               end if; -- N_Jugando
--               Put("*3*");
            end if; -- Lanzar Count = 0
            ---------------------------------

         or
            accept MuerteSubita(Idd: in Integer; OK: in out Boolean) do
               if (Random(F) < (MAX_HIPPIES / 10)+1 ) then
                  Put_Line("****************************************************************");
                  Put_Line("El hippie " & Integer'Image(Id) & " escapa de la muerte subita y GANA!!");
                  Put_Line("****************************************************************");
                  OK:= False;
                  abort Hippie;
               else
                  Put_Line("****************************************************************");
                  Put_Line("El hippie " & Integer'Image(Id) & " NO escapa de la muerte subita.");
                  Put_Line("****************************************************************");
                  Jugando := False;
                  HippieSabio.EstoyEliminado(Id);
                  OK:= True;
               end if;
            end MuerteSubita;
         end select;
         HippieSabio.N_Elim(N_Jugando);
         Put_Line("*4* - " & Integer'Image(N_Jugando) & "--HIPPIE--> " & Integer'Image(Id));
      end loop; -- while


      Put_Line("*FIN* ---HIPPIE--> " & Integer'Image(Id));
      HippieSabio.EstaEliminado(Id,EliminadoManda);
      Put_Line("Eliminado" & Boolean'Image(EliminadoManda) & " -- " & Integer'Image(Id));
      if (not EliminadoManda) then
         Put_Line("****************************************************************");
         Put_Line("El HIPPIE " & Integer'Image(Id) & " es el GANADOR!");
         Put_Line("****************************************************************");
         abort Hippie;
      end if;

   end Hippie;




begin

   NumeroAleatorio.Reset(F);
   ------------------------------------------------
   -- Necesitamos dos numeros aleatorios diferentes
   -- que seran los que empiecen con un huevo :)
   Aux1 := Random(F);
   Aux2 := Random(F);
   while (Aux1 = Aux2) loop
      Aux2 := NumeroAleatorio.Random(F);
   end loop;
   ------------------------------------------------

   for I in 1..MAX_HIPPIES loop
      Hippies(I).Init(I, 0);
   end loop;


   for I in 1..MAX_HIPPIES loop
      if (I = Aux1 or I = Aux2) then
         Hippies(I).Lanzar_Hacia(0,T);
      end if;
   end loop;




end HippiesCitasAda;

